﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System.Collections.Generic;
using System.Linq;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusCompCore
{
    public class FinPlusAccumulator: FinPlusComponent 
    {
        [State(Test = true)]
        public override bool IsValid { get; set; }
        [State(Test = true)]
        public override string TestString { get { return Adapter.ToString(); } }
        public override IPods Adapter { get { return _adapter; } }
  
        private enum Format { Base, ColDetail, RowDetail, RowTotal, ColTotal, Unknown }
        private Accumulator _base;
        private readonly IDictionary<string, Accumulator> _accum;
        private readonly List<Format> _include;
        private readonly IPods _adapter;
        private readonly string[] _keyLabels;
        private readonly string _name;
        private readonly int _rounding;

        //construct
        public FinPlusAccumulator(string name, string[] keyLabels, List<string> include, int rounding) 
        {
            _name = name;
            _keyLabels = keyLabels;
            _include = include.Select(f=> f.EnumParse<Format>(Format.Unknown)).ToList();
            _rounding = rounding;
            _accum = new Dictionary<string, Accumulator>();
            _adapter = IOC.New<IPods>();
            IsValid = true;
        }

        //public
        public void Refresh() { _adapter.Refresh();}

        public bool TryAdd(string key, object[,] value, int row, bool isBase = false, char keyDelimitor = '|')
        {
            try
            {
                var isColVisible = _include.Contains(Format.ColDetail) && isBase ? _include.Contains(Format.Base) : true;
                var accum = GetAccum(key, keyDelimitor, isColVisible);

                var totalChange = 0.0;
                double val = 0.0;

                if (isBase)
                    _base = accum;

                if (double.TryParse(value[row, value.GetUpperBound(1)].ToString(), out val))
                    totalChange += AddValue(accum, value[row, 0].ToString(), val, _rounding, keyDelimitor, !isBase);
                else
                    return false;

                if (_include.Contains(Format.RowTotal)) 
                    AddValue(accum, _name, totalChange, _rounding, keyDelimitor, false, true);

                return true;
            }
            catch
            {
                return false;
            }
        }

        //private
        private double AddValue(Accumulator accum, string key, double value, int rounding, char keyDelimitor, bool baseDiff, bool isTotal = false)
        {
            if(baseDiff && _base != null) 
                value -= (double)_base.Values.Value(key, 0.0);

            var change = accum.Update(key, value, rounding, isTotal, isTotal, isTotal || _include.Contains(Format.RowDetail));

            return GetAccum(accum.TotalKey, keyDelimitor, true).Update(key, change, rounding, true, isTotal, _include.Contains(Format.ColTotal));
        }

        private Accumulator GetAccum(string key, char keyDelimitor, bool isVisible)
        {
            Accumulator accum;
            if (!_accum.ContainsKey(key))
            {
                var totalKey = key.Substring(0, key.LastIndexOf("|") + 1) + "Total";
                if (_include.Contains(Format.ColTotal) && !_accum.ContainsKey(totalKey))
                    _adapter.Add((_accum[totalKey] = new Accumulator(_name, _keyLabels, totalKey, keyDelimitor, User, true)).Model);

                _accum[key] = accum = new Accumulator(_name, _keyLabels, key, keyDelimitor, User, _include.Contains(Format.ColDetail));
                
                if (isVisible) 
                    _adapter.Add(accum.Model);
            }
            else
            {
                accum = _accum[key];
            }

            return accum;
        }
    }
}
